# 从文件中读取数据
# 文本文件可存储的数据量多得难以置信：天气数据、交通数据、社
# 会经济数据、文学作品等。每当需要分析或修改存储在文件中的信
# 息时，读取文件都很有用，对数据分析应用程序来说尤其如此。例
# 如，可以编写一个这样的程序：读取一个文本文件的内容，重新设
# 置这些数据的格式并将其写入文件，让浏览器能够显示这些内容。
# 要使用文本文件中的信息，首先需要将信息读取到内存中。为此，
# 你可以一次性读取文件的全部内容，也可以以每次一行的方式逐步
# 读取。

# 读取整个文件
# 要读取文件，需要一个包含几行文本的文件。
# pi_digits.txt包含精确到小数点后30位的圆周率值，且在小数点后每10位处换行
# 下面的程序打开并读取这个文件，再将其内容显示到屏幕上

# file_reader.py
with open('pi_digits.txt') as file_object:
	contents = file_object.read()
print(contents)

# 在这个程序中，第一行代码做了大量的工作
# 函数open() 接受一个参数：要打开的文件的名称。
# Python在当前执行的文件所在的目录中查找指定的文件。
# 在本例中，当前运行的是ch10_read_data_from_file.py，因此Python在
# ch10_read_data_from_file.py所在的目录中查找pi_digits.txt。
# 函数open() 返回一个表示文件的对象。在这里，open('pi_digits.txt') 返回一个表
# 示文件pi_digits.txt的对象，Python将该对象赋给file_object 供以后使用。

# 关键字with 在不再需要访问文件后将其关闭。在这个程序中，注意到我们调用了open() ，但没有调用close() 。
# 也可以调用open() 和close() 来打开和关闭文件，但这样做时，如果程序存在bug导致方法close() 未执行，
# 文件将不会关闭。这看似微不足道，但未妥善关闭文件可能导致数据丢失或受损。
# 如果在程序中过早调用close() ，你会发现需要使用文件时它已关闭 （无法访问），这会导致更多的错误。
# 并非在任何情况下都能轻松确定关闭文件的恰当时机，但通过使用前面所示的结构，
# 可让Python去确定：你只管打开文件，并在需要时使用它，Python自会在合适的时候自动将其关闭。

# 有了表示pi_digits.txt的文件对象后，使用方法read() （前述程序的第二行）
# 读取这个文件的全部内容，并将其作为一个长长的字符串赋给变量contents 。
# 这样，通过打印contents 的值，就可将这个文本文件的全部内容显示出来

# 相比于原始文件，该输出唯一不同的地方是末尾多了一个空行。
# 为何会多出这个空行呢？因为read() 到达文件末尾时返回一个空字符串，而将这个空字符串显示出来时就是一个空行。
# 要删除多出来的空行，可在函数调用print() 中使用rstrip() ：
with open('pi_digits.txt') as file_object:
	contents = file_object.read()
print(contents.rstrip())
# Python方法rstrip() 删除字符串末尾的空白。现在，输出与原始文件的内容完全相同

# 文件路径
# 将类似于pi_digits.txt的简单文件名传递给函数open() 时，Python将
# 在当前执行的文件（即.py程序文件）所在的目录中查找。
# 根据你组织文件的方式，有时可能要打开不在程序文件所属目录中
# 的文件。例如，你可能将程序文件存储在了文件夹python_work
# 中，而该文件夹中有一个名为text_files的文件夹用于存储程序文件
# 操作的文本文件。虽然文件夹text_files包含在文件夹python_work
# 中，但仅向open() 传递位于前者中的文件名称也不可行，因为
# Python只在文件夹python_work中查找，而不会在其子文件夹
# text_files中查找。要让Python打开不与程序文件位于同一个目录中
# 的文件，需要提供文件路径，让Python到系统的特定位置去查找。
# 由于文件夹text_files位于文件夹python_work中，可以使用相对文件
# 路径来打开其中的文件。相对文件路径让Python到指定的位置去查
# 找，而该位置是相对于当前运行的程序所在目录的。例如，可这样编写代码：

# with open('text_files/filename.txt') as file_object:
# 这行代码让Python到文件夹python_work下的文件夹text_files中去查找指定的.txt文件。

# 注意 　显示文件路径时，Windows系统使用反斜杠（\ ）而不是斜杠（/ ），但在代码中依然可以使用斜杠。

# 还可以将文件在计算机中的准确位置告诉Python，这样就不用关心
# 当前运行的程序存储在什么地方了。这称为绝对文件路径 。在相
# 对路径行不通时，可使用绝对路径。例如，如果text_files并不在文
# 件夹python_work中，而在文件夹other_files中，则向open() 传递路
# 径'text_files/filename .txt' 行不通，因为Python只在文件
# 夹python_work中查找该位置。为明确指出希望Python到哪里去查
# 找，需要提供完整的路径。
# 绝对路径通常比相对路径长，因此将其赋给一个变量，再将该变量
# 传递给open() 会有所帮助：

# file_path = '/home/ehmatthes/other_files/text_files/_filename_.txt'
# with open(file_path) as file_object:

# 通过使用绝对路径，可读取系统中任何地方的文件。就目前而言，
# 最简单的做法是，要么将数据文件存储在程序文件所在的目录，要
# 么将其存储在程序文件所在目录下的一个文件夹（如text_files）中。

# 注意 　如果在文件路径中直接使用反斜杠，将引发错误，因
# 为反斜杠用于对字符串中的字符进行转义。例如，对于路
# 径"C:\path\to\file.txt" ，其中的\t 将被解读为制表符。
# 如果一定要使用反斜杠，可对路径中的每个反斜杠都进行转
# 义，如"C:\\path\\to\\file.txt" 。

# 逐行读取
# 读取文件时，常常需要检查其中的每一行：可能要在文件中查找特
# 定的信息，或者要以某种方式修改文件中的文本。例如，你可能要
# 遍历一个包含天气数据的文件，并使用天气描述中包含sunny字样
# 的行。在新闻报道中，你可能会查找包含标签<headline> 的行，
# 并按特定的格式设置它。
# 要以每次一行的方式检查文件，可对文件对象使用for 循环：

# file_reader.py
filename = 'pi_digits.txt'

with open(filename) as file_object:
	for line in file_object:
		print(line)

# 为查看文件的内容，通过对文件对象执行循环来遍历文件中的每一行
# 打印每一行时，发现空白行更多了,这是因为在这个文件中，每行的末尾都有一
# 个看不见的换行符，而函数调用print() 也会加上一个换行符，因
# 此每行末尾都有两个换行符：一个来自文件，另一个来自函数调用print() 。
# 要消除这些多余的空白行，可在函数调用print() 中使用rstrip()
filename = 'pi_digits.txt'

with open(filename) as file_object:
	for line in file_object:
		print(line.rstrip())


# 创建一个包含文件各行内容的列表
# 使用关键字with 时，open() 返回的文件对象只在with 代码块内
# 可用。如果要在with 代码块外访问文件的内容，可在with 代码块
# 内将文件的各行存储在一个列表中，并在with 代码块外使用该列
# 表：可以立即处理文件的各个部分，也可以推迟到程序后面再处理。
# 下面的示例在with 代码块中将文件pi_digits.txt的各行存储在一个
# 列表中，再在with 代码块外打印：
filename = 'pi_digits.txt'

with open(filename) as file_object:
	lines = file_object.readlines()

for line in lines:
	print(line.rstrip())

# 方法readlines() 从文件中读取每一行，并将其存储在一个列表中。
# 接下来，该列表被赋给变量lines 。在with 代码块外，依然可使用这个变量。
# 使用一个简单的for 循环来打印lines 中的各行。因为列表lines 的每个元素都对应于文件中的一行，
# 所以输出与文件内容完全一致。


# 使用文件的内容
# 以简单的方式使用圆周率的值。首先，创建一个字符串，它包含文件中存储的所有数字，且没有任何空格：
# pi_string.py

filename = 'pi_digits.txt'

with open(filename) as file_object:
	lines = file_object.readlines()

pi_string = ''
for line in lines:
	pi_string += line.rstrip()

print(pi_string)
print(len(pi_string))

# 变量pi_string 指向的字符串包含原来位于每行左边的空格，为删
# 除这些空格，可使用strip() 而非rstrip()
filename = 'pi_digits.txt'

with open(filename) as file_object:
	lines = file_object.readlines()

pi_string = ''
for line in lines:
	pi_string += line.strip()

print(pi_string)
print(len(pi_string))
# 这样就获得了一个字符串，其中包含准确到30位小数的圆周率值。
# 这个字符串长32字符，因为它还包含整数部分的3和小数点

# 注意 　读取文本文件时，Python将其中的所有文本都解读为字
# 符串。如果读取的是数，并要将其作为数值使用，就必须使用
# 函数int() 将其转换为整数或使用函数float() 将其转换为浮点数。



# 包含一百万位的大型文件
# 前面分析的都是一个只有三行的文本文件，但这些代码示例也可处
# 理大得多的文件。如果我们有一个文本文件，其中包含精确到小数
# 点后1 000 000位而不是30位的圆周率值，也可创建一个包含所有这
# 些数字的字符串。为此，无须对前面的程序做任何修改，只要将这
# 个文件传递给它即可。在这里，只打印到小数点后50位，以免终端
# 为显示全部1 000 000位而不断滚动：
# pi_string.py
file_name = 'pi_million_digits.txt'

with open(file_name) as file_object:
	lines = file_object.readlines()

pi_string = ''
for line in lines:
	pi_string += line.strip()

print(f"{pi_string[:52]}...")
print(len(pi_string))
# 对于可处理的数据量，Python没有任何限制。只要系统的内存足够多，你想处理多少数据都可以。



# 　圆周率值中包含你的生日吗
# 我一直想知道自己的生日是否包含在圆周率值中。下面来扩展刚才
# 编写的程序，以确定某个人的生日是否包含在圆周率值的前1 000
# 000位中。为此，可将生日表示为一个由数字组成的字符串，再检
# 查这个字符串是否包含在pi_string 中：

file_name = 'pi_million_digits.txt'

with open(file_name) as file_object:
	lines = file_object.readlines()

pi_string = ''
for line in lines:
	pi_string += line.strip()

birthday = input("Please enter your birthday, in the form mmddyy: ")
if birthday in pi_string:
	print("Your birthday appears in the first million digits of pi!")
else:
	print("Your birthday does not appear in the first million digits of pi!")


	